#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#include <vector>

#include "CompileGraphVis.h"
#include "Ast.h"


CompileGraphViz::CompileGraphViz(const char * outputfilename)
{
  char * fn=(char*)malloc(strlen(outputfilename)+5);
  strcpy(fn,outputfilename);
  strcat(fn,".dot");
  file=fopen(fn,"w");
  assert(file);
  fprintf(file,"digraph G {\n");
  //fprintf(file,"rankdir=LR;\n");
  fprintf(file,"node [ shape=box ];\n");
  free(fn);
  doingOnRoot=true;
}

int CompileGraphViz::finalize()
{
  fprintf(file,"}");
  fclose(file);
  doingOnRoot=true;
  return 0;
}

int CompileGraphViz::compile(AstNodeStatementList * node)
{
  bool here=doingOnRoot;
  doingOnRoot=false;
  std::vector<AstNode*> & nodes=node->getChildren();

  if(!here) 
    fprintf(file,"node_%p [ label = \"stmt list\"];\n",node);
  else {
    fprintf(file,"{ rank=same;\n");
    for(u2 i=0;i<nodes.size();i++) 
      fprintf(file,"node_%p;\n",nodes[i]);
    fprintf(file,"}\n");

    fprintf(file,"node_%p",nodes[0]);
    for(u2 i=1;i<nodes.size();i++) 
      fprintf(file," -> node_%p",nodes[i]);
    fprintf(file,";\n");
  }

  for(u2 i=0;i<nodes.size();i++) {
    if(!here) 
      fprintf(file,"node_%p -> node_%p;\n",node,nodes[i]);
    nodes[i]->compile(this);
  }
  return 0;
}

int CompileGraphViz::compile(AstNodeLiteral* node)
{
  fprintf(file,"node_%p [label = \"%s\"];\n",node,node->getString());
  return 0;
}

int CompileGraphViz::compile(AstNodeLValue* node)
{
  fprintf(file,"node_%p [label = \"lvalue:%s\"];\n",node,node->getSymbol());
  return 0;
}

int CompileGraphViz::compile(AstNodeBooleanOperator * node)
{
  u1 oper=node->getOper();
  AstNodeBooleanOperator * left=node->getLeft();
  AstNodeBooleanOperator * rite=node->getRite();
  AstNodeCompareExpression * cmp=node->getCmp();

  if(cmp) {
    fprintf(file,"node_%p -> node_%p;\n",node,cmp);
    cmp->compile(this);
  } else {
    switch(oper) {
      case AstNodeBooleanOperator::_and:
        fprintf(file,"node_%p [label = \"and\"];\n",node);

        if(left) {
          fprintf(file,"node_%p -> node_%p;\n",node,left);
          left->compile(this);
        }

        if(rite) {
          fprintf(file,"node_%p -> node_%p;\n",node,rite);
          rite->compile(this);
        }
        break;
      case AstNodeBooleanOperator::_or:
        fprintf(file,"node_%p [label = \"or\"];\n",node);

        if(left) {
          fprintf(file,"node_%p -> node_%p;\n",node,left);
          left->compile(this);
        }

        if(rite) {
          fprintf(file,"node_%p -> node_%p;\n",node,rite);
          rite->compile(this);
        }
        break;
      case AstNodeBooleanOperator::_not:
        fprintf(file,"node_%p [label = \"not\"];\n",node);
        if(left) {
          fprintf(file,"node_%p -> node_%p;\n",node,left);
          left->compile(this);
        }
        break;
      case AstNodeBooleanOperator::_nop:
        fprintf(file,"node_%p [label = \"nop\"];\n",node);
        break;
    };
  }
  return 0;
}


int CompileGraphViz::compile(AstNodeAssignment * node)
{
  fprintf(file,"node_%p [ label=\"assignment\" ];\n",node);
  fprintf(file,"node_%p -> node_%p;\n",node,node->getLValue());
  fprintf(file,"node_%p -> node_%p;\n",node,node->getExpression());

  node->getLValue()->compile(this);
  node->getExpression()->compile(this);
  return 0;
}

int CompileGraphViz::compile(AstNodeExpressionVariable * node)
{
  fprintf(file,"node_%p [label = \"variable:%s\"];\n",node,node->getName());
  return 0;
}

int CompileGraphViz::compile(AstNodeDoubleConstant * node)
{
  fprintf(file,"node_%p [label = \"double:%lf\"];\n",node,node->getValue());
  return 0;
}

int CompileGraphViz::compile(AstNodeBinaryOperator * node)
{
  int oper=node->getOperator();
  AstNodeExpression * left=node->getLeft();
  AstNodeExpression * rite=node->getRite();

  if(isprint(oper)) 
    fprintf(file,"node_%p [label = \"binop:%c\"];\n",node,oper);
  else
    fprintf(file,"node_%p [label = \"binop:%d\"];\n",node,oper);

  fprintf(file,"node_%p -> node_%p;\n",node,left);
  fprintf(file,"node_%p -> node_%p;\n",node,rite);

  left->compile(this);
  rite->compile(this);
  return 0;
}

int CompileGraphViz::compile(AstNodeCompareExpression * node)
{
  u1 oper=node->getOper();
  AstNodeExpression * left=node->getLeft();
  AstNodeExpression * rite=node->getRite();
   
  switch(oper) {
    case AstNodeCompareExpression::_equal:
      fprintf(file,"node_%p [label = \"cmp==\"];\n",node);
      break;
    case AstNodeCompareExpression::_less:
      fprintf(file,"node_%p [label = \"cmp<\"];\n",node);
      break;
    case AstNodeCompareExpression::_greater:
      fprintf(file,"node_%p [label = \"cmp>\"];\n",node);
      break;
    case AstNodeCompareExpression::_less_equal:
      fprintf(file,"node_%p [label = \"cmp<=\"];\n",node);
      break;
    case AstNodeCompareExpression::_greater_equal:
      fprintf(file,"node_%p [label = \"cmp>=\"];\n",node);
      break;
    case AstNodeCompareExpression::_not_equal:
      fprintf(file,"node_%p [label = \"cmp!=\"];\n",node);
      break;
    default:
      assert(0);
  }
  fprintf(file,"node_%p -> node_%p;\n",node,left);
  fprintf(file,"node_%p -> node_%p;\n",node,rite);
  left->compile(this);
  rite->compile(this);
  return 0;
}

int CompileGraphViz::compile(AstNodeForStatement * node)
{
  AstNode *                startNode=node->getStartNode();
  AstNodeBooleanOperator * condNode=node->getCond();
  AstNode *                nextNode=node->getNextNode();
  AstNode *                bodyNode=node->getBody();

  fprintf(file,"node_%p [shape=record,label = \"<f0> for | <f1> start | <f2> condition | <f3> next | <f4> body\"];\n",node);
  fprintf(file,"node_%p:f1 -> node_%p;\n",node,startNode);
  fprintf(file,"node_%p:f1 -> node_%p;\n",node,condNode);
  fprintf(file,"node_%p:f1 -> node_%p;\n",node,nextNode);
  fprintf(file,"node_%p:f1 -> node_%p;\n",node,bodyNode);

  startNode->compile(this);
  condNode->compile(this);
  nextNode->compile(this);
  bodyNode->compile(this);
  return 0;
}
int CompileGraphViz::compile(AstNodeIfStatement * node)
{
  AstNodeBooleanOperator * condBody=node->getCondition();
  AstNode * trueBody=node->getTrueBody();
  AstNode * falseBody=node->getFalseBody();

  if(falseBody) 
    fprintf(file,"node_%p [shape=record,label = \"<f0> if | <f1> cond | <f2> true_body | <f3> false_body\"];\n",node);
  else
    fprintf(file,"node_%p [shape=record,label = \"<f0> if | <f1> cond | <f2> true_body\"];\n",node);

  fprintf(file,"node_%p:f1 -> node_%p;\n",node,condBody);
  fprintf(file,"node_%p:f2 -> node_%p;\n",node,trueBody);
  if(falseBody)
    fprintf(file,"node_%p:f3 -> node_%p;\n",node,falseBody);

  condBody->compile(this);
  trueBody->compile(this);
  if(falseBody)
    falseBody->compile(this);
  return 0;
}

int CompileGraphViz::compile(AstNodePrintLiteral * node)
{
  fprintf(file,"node_%p [label = \"print_literal\"];\n",node);
  return 0;
}

int CompileGraphViz::compile(AstNodePrintExpression * node)
{
  assert(0);
}

